﻿using System;
using System.Text;
using System.Text.RegularExpressions;

namespace Softio.Mscd.Client
{
    internal class MinifiedControlsHelper
    {
        private static Regex stripJavaScriptComments = new Regex(
                @"/\*.+?\*/",
                RegexOptions.Singleline | RegexOptions.Compiled);

        private static readonly Regex removeWhitespaceBetweenTags = new Regex(@">\s+<", RegexOptions.Compiled);
        private static readonly Regex removeLinebreaks = new Regex(@"\n\s+", RegexOptions.Compiled);

        internal static string Minify(StaticFile.ContentTypes contentType, string input)
        {
            switch (contentType)
            {
                case StaticFile.ContentTypes.JavaScript:
                    return 
                        MinifyJavaScript(input)
                        .Replace("</script>", "\n</script>");

                case StaticFile.ContentTypes.Stylesheet:
                    return MinifyStylesheet(input);

                case StaticFile.ContentTypes.Other: //HTML
                    return MinifyHtml(input);
            }
            return input;
        }

        private static string MinifyHtml(string input)
        {
            input = removeWhitespaceBetweenTags.Replace(input, "> <");
            input = removeLinebreaks.Replace(input, string.Empty);
            return input;
                
        }

        private static string MinifyStylesheet(string input)
        {
            string body = Regex.Replace(input, @"[a-zA-Z]+#", "#");
            body = Regex.Replace(body, @"[\n\r]+\s*", string.Empty);
            body = Regex.Replace(body, @"\s+", " ");
            body = Regex.Replace(body, @"\s?([:,;{}])\s?", "$1");
            body = body.Replace(";}", "}");
            body = Regex.Replace(body, @"([\s:]0)(px|pt|%|em)", "$1");

            // Remove comments from CSS
            body = Regex.Replace(body, @"/\*[\d\D]*?\*/", string.Empty);
            return body;
        }

        private static string MinifyJavaScript(string body)
        {
            string[] lines = body.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
            StringBuilder emptyLines = new StringBuilder(body.Length);
            foreach (string line in lines)
            {
                string s = line.Trim();
                if (s.Length > 0 && !s.StartsWith("//"))
                {
                    emptyLines.AppendLine(s.Trim());
                }
            }

            body = stripJavaScriptComments.Replace(emptyLines.ToString(), "");
            // remove C styles comments
            body = Regex.Replace(body, "/\\*.*?\\*/", String.Empty, RegexOptions.Compiled | RegexOptions.Singleline);
            //// trim left
            body = Regex.Replace(body, "^\\s*", String.Empty, RegexOptions.Compiled | RegexOptions.Multiline);
            //// trim right
            body = Regex.Replace(body, "\\s*[\\r\\n]", "\r\n", RegexOptions.Compiled | RegexOptions.ECMAScript);
            // remove whitespace beside of left curly braced
            body = Regex.Replace(body, "\\s*{\\s*", "{", RegexOptions.Compiled | RegexOptions.ECMAScript);
            // remove whitespace beside of coma
            body = Regex.Replace(body, "\\s*,\\s*", ",", RegexOptions.Compiled | RegexOptions.ECMAScript);
            // remove whitespace beside of semicolon
            body = Regex.Replace(body, "\\s*;\\s*", ";", RegexOptions.Compiled | RegexOptions.ECMAScript);
            // remove newline after keywords
            body = Regex.Replace(body, "\\n(?<=\\b(abstract|boolean|break|byte|case|catch|char|class|const|continue|default|delete|do|double|else|extends|false|final|finally|float|for|function|goto|if|implements|import|in|instanceof|int|interface|long|native|new|null|package|private|protected|public|return|short|static|super|switch|synchronized|this|throw|throws|transient|true|try|typeof|var|void|while|with)\\n)", " ", RegexOptions.Compiled | RegexOptions.ECMAScript);

            body = Regex.Replace(body, @"^[\s]+|[ \f\r\t\v]+$", String.Empty);
            body = Regex.Replace(body, @"([+-])\n\1", "$1 $1");
            body = Regex.Replace(body, @"([^+-][+-])\n", "$1");
            body = Regex.Replace(body, @"([^+]) ?(\+)", "$1$2");
            body = Regex.Replace(body, @"(\+) ?([^+])", "$1$2");
            body = Regex.Replace(body, @"([^-]) ?(\-)", "$1$2");
            body = Regex.Replace(body, @"(\-) ?([^-])", "$1$2");
            body = Regex.Replace(body, @"\n([{}()[\],<>/*%&|^!~?:=.;+-])", "$1");
            body = Regex.Replace(body, @"[ \f\r\t\v]?([\n\xFE\xFF/{}[\];,<>*%&|^!~?:=])[\f\r\t\v]?", "$1");
            body = Regex.Replace(body, @"(\W(if|while|for)\([^{]*?\))\n", "$1");
            body = Regex.Replace(body, @"(\W(if|while|for)\([^{]*?\))((if|while|for)\([^{]*?\))\n", "$1$3");
            body = Regex.Replace(body, @"([;}]else)\n", "$1 ");
            body = Regex.Replace(body, @"(?<=[>])\s{2,}(?=[<])|(?<=[>])\s{2,}(?=&nbsp;)|(?<=&ndsp;)\s{2,}(?=[<])", String.Empty);

            return body;
        }
    }
}
